import java.io.File;
import java.io.Serializable;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.geotools.data.*;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geojson.feature.FeatureJSON;
import org.geotools.geojson.geom.GeometryJSON;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.styling.SLD;
import org.geotools.styling.Style;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Polygon;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import utilityTools.BaseGeometryUtility;
import utilityTools.GeoJsonUtiles;

public class Main {
    public static void main(String[] args) {
        try {
            System.out.println("Maven管理的Java普通项目");
//            针对Java原生理论进行测试
//            JavaOperations javaOperations=new JavaOperations();
//            javaOperations.testListDelete();
//            javaOperations.testPointEquals();
//            javaOperations.testUsingHashCode();
//            javaOperations.test二维数组();
//            javaOperations.test计算概率分布();
//            javaOperations.testJava的复制();
//            javaOperations.test除法计算();
//            javaOperations.test度转为弧度();
//            javaOperations.test字符串分割();
//            javaOperations.testImageConversion();
            //针对GeoTools的工具进行测试
//            GeotoolsOperation operation=new GeotoolsOperation() ;
//            operation.testUnion();
//            operation.testGeoOutput();
//            operation.testWkt();
//            operation.testReadWkt();
//            operation.test空间权重矩阵();
//            operation.calcDistance();
//            operation.testEPSG坐标系();
//            operation.testxy2vector();
//            operation.test几何类型判断();
//            operation.test四叉树空间索引();
//            operation.test普通空间查询();
//            operation.testKdTree索引();
//            operation.test判断String是wkt或geojson();
//            operation.test获取PostGIS数据();
            //针对shp相关的操作测试
//            ShpOperation shpOperation=new ShpOperation();
//            shpOperation.testGetPropertiesNoGeometry();
//            shpOperation.testShpToExcel();
//            shpOperation.testShpEdit();
            //其他测试
//            GDALOperation operation=new GDALOperation();
//            operation.getRasterGeoTransform();
//            operation.getRasterMetadata();
//            operation.testCreateRaster();
            String pathSLD="E:\\data\\sld06.sld";
//            CreateSLD sld=new CreateSLD();
//            sld.createSLD_Polygon("the_geom",pathSLD);
//            sld.createSLD_Point(pathSLD);
            CreateSLD3 sld3=new CreateSLD3();
            sld3.createSLD_Polygon("the_geom",pathSLD);
        } catch (Exception error) {
            System.out.println("系统错误：" + error.getMessage());
            System.out.println("错误堆栈信息如下：");
            error.printStackTrace();
        }
    }

    private static void testGeojson2Shp(){
        try {
            //GeoJson数据读取及导出到shp文件
//            String pathGeoJson = "E:\\9.1GISData\\省市级行政区[GeoJson类型]\\mygeodatas.json";
            String pathTest= "E:\\9.1GISData\\myjsondemo.json";
            ReadGeoJson2Shp readGeoJson2Shp = new ReadGeoJson2Shp(pathTest);
            readGeoJson2Shp.readGeoJsonSource();
            System.out.println("GeoJson数据读取完毕");
        }catch (Exception error){
            System.out.printf("执行错误："+error.getMessage());
            error.printStackTrace();
        }
    }
    /**
     * 执行坐标转换（4326至3857）
     * 结果：shp中有一个要素（180.0,90.0）无法执行转换，会报错‘太靠近pole’;剔除这个要素之后其他的要素都可以转换成功
     *      并且能够在PostgreSQL里面看到几何
     */
    private static void testCoordTransform() {
        try {
            String wktPoint = "POINT(100.02715479879 33.462715497945)";
            String wktLine = "LINESTRING(108.32803893589 41.306670233001,99.950999898452 25.84722546391)";
            String wktPolygon = "POLYGON((100.02715479879 32.168082192159,102.76873121104 37.194305614622,"
                    + "107.0334056301 34.909658604412,105.96723702534 30.949603786713,"
                    + "100.02715479879 32.168082192159))";
//            String pointTest="POINT(180.0 90.0)";
            long timeStart=System.currentTimeMillis();
            CoordTransform myCoordTrans = new CoordTransform();
            //单个几何转换测试
            System.out.println("点转换结果" + myCoordTrans.projectGeometry(wktPoint));
            System.out.println("线转换结果" + myCoordTrans.projectGeometry(wktLine));
            System.out.println("面转换结果" + myCoordTrans.projectGeometry(wktPolygon));
//            System.out.println("面转换结果" + myCoordTrans.projectGeometry(pointTest));
            //shp文件转换测试
            //排除出错的那个点之后，可以转换成功，并且在PostGIS中可以看到转换后的结果。
            String pathShpSource = "E:\\0.myWork\\testdata\\country.shp";
            String pathShpTarget = "E:\\0.myWork\\testdata\\country2.shp";
            myCoordTrans.projectShp(pathShpSource,pathShpTarget);
            long timeTotal=System.currentTimeMillis()-timeStart;
            System.out.println("shp文件转换执行完毕。一共耗时" + timeTotal/1000 + "s");
        } catch (Exception error) {
            System.out.println("坐标转换发生错误:");
            error.printStackTrace();
        }
    }
    
    /**
     * 矢量要素编辑操作
     * 1-根据属性查询；2-根据fid查询；
     * 3-矢量要素属性编辑
     * 4-根据空间范围查询符合条件的要素；
     * 5-矢量要素几何位置编辑
     */
    private static void testVectorOperation() {
        try {
            VectorOperation opraVector = new VectorOperation();
            String className = "country";
            /*//根据属性选择要素
            opraVector.queryVectorbyAttrs();
            //编辑要素属性
            opraVector.editVectorAttrs(className, fidFeature, fieldName2Alter, fieldValue2Alter);
            */
            /*//(3)根据空间范围选择矢量要素
            //这里的polygon字符串必须要闭合
            String wktPolygonStr = "POLYGON((117.110858 40.70836,117.286401 40.660719,"
                    + "117.389879 40.228141,117.389879 40.5617,117.110858 40.70836))";
            GeometryFactory geoFactory = JTSFactoryFinder.getGeometryFactory();
            WKTReader wktReader = new WKTReader(geoFactory);
            Geometry geoWKT = wktReader.read(wktPolygonStr);
            opraVector.queryVectorbySpatial(className, geoWKT);
            */
            // System.out.println("坐标参考系是："+geoWKT.getSRID());
            // 这里坐标参考是0，那么疑问是：在操作时用的是什么坐标系呢？
            /*//使用GeoJson的方式求交集
            String strGeoJson= BaseGeometryUtility.Wkt2GeoJson(wktPolygonStr);
            System.out.println("GeoJson结果是："+strGeoJson);
            opraVector.queryVectorbySpatial(className, strGeoJson);
            */
            //遗留的问题：我创建的几何是JTS的类型，而filter需要的是GeoTools的类型，
            //且这两者之间又不能直接转换，那么有什么方法可以执行呢？
            //(4)矢量要素几何编辑（两种方式都可以实现要素几何的编辑）
            String wktPoint = "POINT(121.472644 31.231706)";
            //方式一[使用wkt的几何格式]
            //GeometryFactory factoryGeo = JTSFactoryFinder.getGeometryFactory();
            //WKTReader readerWkt = new WKTReader(factoryGeo);
            //Geometry geoWKT = readerWkt.read(wktPoint);
            //方式二[使用GeoJson的几何格式]
            String strGeoJson= BaseGeometryUtility.Wkt2GeoJson(wktPoint);
            opraVector.editVectorGeometry("cities",2,strGeoJson);
            System.out.println("矢量要素编辑 执行结束");
        } catch (Exception error) {
            System.out.println("执行过程发生异常：");
            error.printStackTrace();
        }
    }

    /**
     * 删除指定路径下文件夹中的某些文件
     *
     * @param pInputfile：File文件类型，可以是路径或者直接是单个文件
     */
    private static void myDelete(File pInputfile) {
        if (null == pInputfile) {
            System.out.print("文件or文件夹为空，不需要删除.");
            return;
        }
        if (pInputfile.isDirectory()) {
            File[] files = pInputfile.listFiles();
            if (null != files) {
                for (File childFile : files) {
                    myDelete(childFile);
                }
            }
        } else {
            if (pInputfile.getName().equals("_remote.repositories") || pInputfile.getName().endsWith(".lastUpdated")) {
                pInputfile.delete();
            }
        }
    }

    /**
     * 测试GeoJson结构相关的操作
     * @throws Exception
     */
    private static void testFeaturetoGeojson() throws Exception {
        String path = "E:\\9.1GISData\\mygeo2shp.shp";
        String path2 = "E:\\9.1GISData\\Lambert\\china.shp";
        FeatureJSON fjson = new FeatureJSON();
        StringWriter feaWriterOut = new StringWriter();
        GeometryJSON gjson = new GeometryJSON(8);//坐标保留8位小数
        StringWriter geoWriterOut = new StringWriter();
        //原始数据读取
        SimpleFeatureSource feaSource = ((SimpleFeatureSource) ShpfileReader.getShpfile2(path,"utf8"));
        SimpleFeatureType schemaSource = feaSource.getSchema();
        FeatureCollection feaCollection = feaSource.getFeatures();
        FeatureIterator feaIterator = feaCollection.features();
        SimpleFeature featureSource = ((SimpleFeature) feaIterator.next());
        //feature的整体返回测试
        fjson.writeFeature(featureSource, feaWriterOut);//这样的结果是直接将一个feature输出为一个完整的GeoJson文件
        System.out.println("输出结果1：");
        System.out.println("长度1：" + feaWriterOut.toString().length());
        System.out.println(feaWriterOut.toString());
        Geometry geometry = ((Geometry) featureSource.getDefaultGeometry());
        //geometry单独返回测试
        //使用GeometryJSON得到的直接是Feature中的Geometry的Json结果（也就是geometry键对应的值）
        gjson.write(geometry, geoWriterOut);
        System.out.println("几何结果是");
        System.out.println(geoWriterOut.toString());
        System.out.println("坐标系代码：" + geometry.getSRID());
//        fjson.writeFeature(featureSource,writerOut);
        //两次的String字符串可以叠加，但是仅仅是机械的将两个Json完整字符串完全相加，结果反而是错误的。因此不能机械的这么做
        //而且所有的write都是这么操作的，因此一个Stringwrite最好不要这么样去添加内容


        //也可以直接建立集合，然后将要素放在集合中，最后统一将一个集合输出为GeoJson对象
        StringWriter collWriterOut = new StringWriter();
        DefaultFeatureCollection result = new DefaultFeatureCollection();//只要一加参数就会报错警告，为什么？
        //创建新的要素
        result.add(featureSource);
        featureSource = ((SimpleFeature) feaIterator.next());
        result.add(featureSource);
        fjson.writeFeatureCollection(result, collWriterOut);//这一句执行起来是比较耗时的
        System.out.println("要素集合输出结果");
        System.out.println(collWriterOut.toString());

        //geojson也可以是几何的集合
        StringWriter ttt=new StringWriter();
        ArrayList<Geometry> buffers = new ArrayList<>();
        buffers.add(geometry);
        buffers.add(((Geometry) featureSource.getDefaultGeometry()));
        Geometry[] tgeos=new Geometry[buffers.size()];
        buffers.toArray(tgeos);
        gjson.writeGeometryCollection(new GeometryCollection(tgeos,new GeometryFactory()),ttt);
        System.out.println("几何集合的输出：");
        System.out.println(ttt.toString());
    }

    private static void testShpOperation()throws Exception{
        String pathRoot="E:\\9.1GISData\\";
        String path = "E:\\9.1GISData\\Lambert\\地级城市驻地.shp";
        SimpleFeatureSource sfsSource= ((SimpleFeatureSource) ShpfileReader.getShpfile2(path,"GBK"));
//        System.out.println(feature.getDefaultGeometryProperty().getName().toString());//可以正确获取几何列的名称
//        calcBuffer_shp(sfsSource,100);
        Style style = SLD.createSimpleStyle(sfsSource.getSchema());
        Layer layerShp=new FeatureLayer(sfsSource,style);
        DataStore layerDataStore= (DataStore) layerShp.getFeatureSource().getDataStore();
        if (layerDataStore==null) {
            System.out.println("获取数据为空");
            return;
        }else {
            //可以这么来判断
            ShapefileDataStore shpDataStore= layerDataStore instanceof ShapefileDataStore ? ((ShapefileDataStore) layerDataStore) : null;
//            System.out.println(shpDS.getDataStoreFactory());//null，获取不到factory对象，也就没法获取参数
            System.out.println(shpDataStore.toString());//返回很多参数，其中包含了shp的路径,且自身不是一个Json字符串
            String[] values1=shpDataStore.toString().split(",");//分割的结果不是可用数组
            String[] values2=shpDataStore.toString().split(" ");
//            System.out.println(shpDS.getNames().get(0));//shp的名称
//            System.out.println("shpds的uri："+shpDS.getNamespaceURI());//空
            System.out.println("===========");
            ServiceInfo serviceInfo= layerDataStore.getInfo();//new NullProgressListener()，这个参数必须用在iservice接口中
            System.out.println(layerDataStore.getTypeNames().length);
            System.out.println(layerDataStore.getTypeNames()[0]);//也是shp的名字
            System.out.println("Name Size："+layerDataStore.getNames());//shp的名字
            System.out.println("Schema的内容："+serviceInfo.getSchema().getPath());//schema是一个网址
            System.out.println("Description:"+serviceInfo.getDescription());//Features from ShapefileDataStore
//            System.out.println("Schema的内容："+serviceInfo.getSchema());//是一个GeoTools的网址
            //            System.out.println(serviceInfo.getPublisher());//null
////            System.out.println("Title的内容："+serviceInfo.getSource());//null
////            System.out.println("Title的内容："+serviceInfo.getTitle());//null
        }

        //直接使用SimpleFeatureSource获取的信息
//        ResourceInfo info= sfs2.getInfo();
//        if (info==null) {
//            System.out.println("getInfo无法获取信息");
//        }else {
////            System.out.println("URI的内容："+info.getSchema());//getSchema是空
//            System.out.println("Title的内容："+info.getTitle());//shp文件名
//            System.out.println("Description的内容："+info.getDescription());//空
//            System.out.println("Name的内容："+info.getName());//shp文件名
//            System.out.println("info自身的内容："+info.toString());//org.geotools.data.store.ContentFeatureSource$1@50ecde95
//        }

        System.out.println("成功执行");


        //其他测试
//        ResourceInfo infoSource= sfsSource.getInfo();
//        System.out.println(infoSource.getDescription());
//        System.out.println(infoSource.getTitle());
//        System.out.println("===分隔符===");
//        //下面3个方式得到的结果是一样的，都是shp不带扩展名的文件名
////        System.out.println(sfsSource.getName().toString());
////        System.out.println(sfsSource.getName().getURI());
////        System.out.println(sfsSource.getInfo().getName().toString());
//        SimpleFeatureType typeSource=sfsSource.getSchema();
//        String fileNameBuffer=typeSource.getName().toString()+"_buffer";
//        String pathNew=pathRoot+fileNameBuffer+".shp";
//        //判断指定路径下是否存在相同的文件
//        File file=new File(pathNew);
//        if (file.exists()) {
//            System.out.println("已经存在同名文件");
//            fileNameBuffer=fileNameBuffer+"_buffer";
//            pathNew=pathRoot+fileNameBuffer+".shp";
//        }
    }
    private static void testPGOperation()throws Exception{
        TblVectorData tmp=new TblVectorData();
        tmp.setCrs("1111");
        tmp.setName("name1");
        tmp.setDataType("Shapefile");
        tmp.setFilePath("E:\\9.1GISData\\Lambert\\地级城市驻地.shp");
        JSONObject jo= (JSONObject) JSONObject.toJSON(tmp);
        System.out.println("转换成的Json结果：");
        System.out.println(jo.toString());
    }

    //将度换算成米，公式为：degree = meter / (2 * Math.PI * 6371004) * 360
    //因为GeoTools中在计算缓冲区时，使用的是度，所以需要根据实际来进行转换
    public static void calcBuffer_shp(SimpleFeatureSource layerInput, double distanceInput) throws Exception {
        //将输入单位m转换为度
        double distanceDegree = Math.pow((Math.pow(distanceInput, -1)) * 111319.49079327358, -1);
//        double distanceDegree = distanceInput / (2 * Math.PI * 6371004) * 360;
        SimpleFeatureType typeSource=layerInput.getSchema();
        String geoTypeName=typeSource.getGeometryDescriptor().getName().toString();
        List<AttributeDescriptor> attrsTarget=new ArrayList<>();
        for (AttributeDescriptor perAttr : typeSource.getAttributeDescriptors()) {
            if (perAttr.getName().toString().equalsIgnoreCase(geoTypeName)) {
                continue;
            }
            attrsTarget.add(perAttr);
        }
        //shp时返回的结果是在服务器默认工作空间下创建一个shp-buffer文件
        ShapefileDataStore dsTarget=getBufferShp(layerInput);
        FeatureWriter<SimpleFeatureType, SimpleFeature> writerTarget = dsTarget.getFeatureWriter(dsTarget.getTypeNames()[0],
                Transaction.AUTO_COMMIT);
        //结果集结构建立
        SimpleFeatureCollection featureCollection= layerInput.getFeatures();
        SimpleFeatureIterator iteratorSource= featureCollection.features();
        int tmpCounter=0;
        while (iteratorSource.hasNext()){
            tmpCounter++;
            SimpleFeature perFeaSource=iteratorSource.next();
            Geometry geoSource= ((Geometry) perFeaSource.getDefaultGeometry());
            Geometry geoBuffer= geoSource.buffer(distanceDegree);//返回polygon或者空
            if (geoBuffer.isEmpty()) {
                System.out.println("几何为空，不能执行");
                continue;
            }
            if (tmpCounter<3) {
                System.out.println("面积是："+geoBuffer.getArea());
                System.out.println("缓冲区计算几何结果："+geoBuffer.getGeometryType());
            }
            SimpleFeature featureBuffer=writerTarget.next();
            //这种方式防止把几何也添加进去
            for (AttributeDescriptor perAttrTarget : attrsTarget) {
                featureBuffer.setAttribute(perAttrTarget.getName(),perFeaSource.getAttribute(perAttrTarget.getName()));
            }
            featureBuffer.setDefaultGeometry(geoBuffer);
            writerTarget.write();
        }
        iteratorSource.close();
        writerTarget.close();
        dsTarget.dispose();
    }

    //创建一个跟源shp结构相同的shpBuffer文件
    private static ShapefileDataStore getBufferShp(SimpleFeatureSource sfsInput) throws Exception {
        //在服务器默认工作空间创建buffer结果shp文件（暂时作为）
        String pathRoot="E:\\9.1GISData\\";
        String nameSource=sfsInput.getName().toString();
        File fileTarget=getBufferFilename(nameSource,pathRoot);
        //创建目标shape文件对象
        SimpleFeatureType typeSource=sfsInput.getSchema();
        String geoTypeName=typeSource.getGeometryDescriptor().getName().toString();
        List<AttributeDescriptor> attrsTarget=new ArrayList<>();
        for (AttributeDescriptor perAttr : typeSource.getAttributeDescriptors()) {
            if (perAttr.getName().toString().equalsIgnoreCase(geoTypeName)) {
                continue;
            }
            System.out.println(perAttr.getName());
            attrsTarget.add(perAttr);
        }
        System.out.println("几何列的名称："+geoTypeName);
        Map<String, Serializable> params = new HashMap<String, Serializable>();
        FileDataStoreFactorySpi factory = new ShapefileDataStoreFactory();
        params.put(ShapefileDataStoreFactory.URLP.key, fileTarget.toURI().toURL());
        ShapefileDataStore dsTarget = (ShapefileDataStore) factory.createNewDataStore(params);
        SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
        typeBuilder.setName(nameSource);
        typeBuilder.addAll(attrsTarget);//这样是不是把几何也添加进来的？
        System.out.println("坐标系："+typeSource.getCoordinateReferenceSystem().getName().toString());
        typeBuilder.setCRS(typeSource.getCoordinateReferenceSystem());
        typeBuilder.add(geoTypeName, Polygon.class);
        SimpleFeatureType typeTarget=typeBuilder.buildFeatureType();
        System.out.println("新建shp的类型："+typeTarget.getGeometryDescriptor().getType().toString());
        dsTarget.createSchema(typeTarget);
        dsTarget.setCharset(Charset.forName("GBK"));
        return dsTarget;
    }

    private static File getBufferFilename(String nameSource,String pathRoot) {
        String targetNameNoExtention = nameSource + "_buffer1";
        File fileTarget = new File(pathRoot + targetNameNoExtention + ".shp");
        if (fileTarget.exists()) {
            fileTarget = getBufferFilename(targetNameNoExtention, pathRoot);
        }
        return fileTarget;
    }

    private static void testGeojsonOpration() throws Exception {
        String path="E:\\9.1GISData\\geojson_point.json";
        String path2="E:\\9.1GISData\\geojson_polygon.json";
        File fileInput=new File(path);
        JSONObject geojsonSource= GeoJsonUtiles.getGeoJsonSource(fileInput);
        String ttt=geojsonSource.getString("Type");
//        JSONObject ssss= geojsonSource.getJSONObject("properties");//json对象必须一级一级的获取
        JSONArray array=geojsonSource.getJSONArray("features");
        for (Object tmp : array) {
            JSONObject tobj=(JSONObject) tmp;
//            JSONArray propArray= tobj.getJSONArray("properties");//不是每一个Json对象都可以使用getarray的方法
            //获取的array其实也是一系列Json对象的集合
//            for (Object prop : propArray) {
//                System.out.println(prop);
//            }
        }
    }

}


